﻿using AForge.Controls;
using AForge.Video.DirectShow;
using ArcFaceDemo.Entity;
using ArcFaceDemo.SDKModels;
using ArcFaceDemo.SDKUtil;
using ArcFaceDemo.Utils;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Forms;

namespace ArcFaceDemo
{
    public partial class MainFrm : Form
    {
        /// <summary>
        /// 引擎Handle
        /// </summary>
        private IntPtr pImageEngine = IntPtr.Zero;
        /// <summary>
        /// 人脸特征列表
        /// </summary>
        private Dictionary<IntPtr, string> imagesFeatureDictionary = new Dictionary<IntPtr, string>();

        /// <summary>
        /// 相似度阈值
        /// </summary>
        private float threshold = 0.8f;
        /// <summary>
        /// 活体检测引擎，和图片引擎分开，在多线程下可以提高性能
        /// </summary>
        private IntPtr pLiveEngine = IntPtr.Zero;

        private bool needCamPic = false;//需要从摄像头截取图片
        private object mylock = new object();//needCamPic变量的锁
        public MainFrm()
        {
            InitializeComponent();
            CheckForIllegalCrossThreadCalls = false;
            try
            {
                //初始化引擎
                InitEngines();
            }
            catch (Exception err)
            {
                MessageBox.Show(err.Message);
            }
        }

        private void InitEngines()
        {
            //读取配置文件
            AppSettingsReader reader = new AppSettingsReader();
            string appId = "";
            string sdkKey32 = "";
            if (Environment.Is64BitProcess)//区分32位和64位的sdk
            {
                MessageBox.Show("请使用32位编译");
                return;
            }

            //在线激活引擎
            int retCode = 0;
            try
            {
                retCode = ASFFunctions.ASFActivation(appId, sdkKey32);
            }
            catch (Exception ex)
            {
                if (ex.Message.Contains("无法加载 DLL"))
                {
                    MessageBox.Show("请将sdk相关DLL放入bin文件夹中!");
                }
                else
                {
                    MessageBox.Show("激活引擎失败!");
                }
                return;
            }
            uint detectMode = DetectionMode.ASF_DETECT_MODE_IMAGE;//引擎模式
            //int imageDetectFaceOrientPriority = ASF_OrientPriority.ASF_OP_0_ONLY;  //Image模式下检测脸部的角度优先值
            int imageDetectFaceOrientPriority = ASF_OrientPriority.ASF_OP_0_HIGHER_EXT;  //Image模式下检测脸部的角度优先值
            int detectFaceScaleVal = 16;//人脸在图片中所占比例，如果需要调整检测人脸尺寸请修改此值，有效数值为2-32
            int detectFaceMaxNum = 5;//最大需要检测的人脸个数
            int combinedMask = FaceEngineMask.ASF_FACE_DETECT | FaceEngineMask.ASF_FACERECOGNITION;//引擎初始化时需要初始化的检测功能组合
            //初始化引擎，正常值为0，其他返回值请参考https://ai.arcsoft.com.cn/manual/docs#/123
            retCode = ASFFunctions.ASFInitEngine(detectMode, imageDetectFaceOrientPriority, detectFaceScaleVal, detectFaceMaxNum, combinedMask, ref pImageEngine);
            AppendText((retCode == 0) ? "人脸引擎初始化成功!\r\n" : string.Format("人脸引擎初始化失败!错误码为:{0}\r\n", retCode));

            combinedMask = FaceEngineMask.ASF_LIVENESS;//活体检测引擎
            retCode = ASFFunctions.ASFInitEngine(detectMode, imageDetectFaceOrientPriority, detectFaceScaleVal, detectFaceMaxNum, combinedMask, ref pLiveEngine);
            AppendText((retCode == 0) ? "活体引擎初始化成功!\r\n" : string.Format("活体引擎初始化失败!错误码为:{0}\r\n", retCode));

            //初始化特征库
            DirectoryInfo dir = new DirectoryInfo("./Features/");
            foreach (var file in dir.GetFiles())
            {
                string name = file.Name;
                byte[] arr = File.ReadAllBytes(file.FullName);
                IntPtr p = FaceUtil.EvetGetPtr(arr);
                imagesFeatureDictionary.Add(p, name);
            }
            bgw.DoWork += Bgw_DoWork;
            bgw.WorkerSupportsCancellation = true;
            bgw.WorkerReportsProgress = true;
            bgw.ProgressChanged += Bgw_ProgressChanged;
            bgw.RunWorkerCompleted += Bgw_RunWorkerCompleted;
        }

        private void btnSelectImageToRegister_Click(object sender, EventArgs e)
        {
            OpenFileDialog openFileDialog = new OpenFileDialog();
            openFileDialog.Title = "选择图片";
            openFileDialog.Filter = "Image File|*.bmp;*.jpg;*.jpeg;*.png";
            openFileDialog.FileName = string.Empty;
            if (openFileDialog.ShowDialog() != DialogResult.OK)
            {
                return;
            }
            string fileName = openFileDialog.FileName;
            if (!CheckImage(fileName, out string errmsg))
            {
                MessageBox.Show(errmsg);
                return;
            }
            try
            {
                Image image = ImageUtil.ReadFromFile(fileName);
                if (image.Width > 1536 || image.Height > 1536)
                {
                    Image image2 = ImageUtil.ScaleImage(image, 1536, 1536);
                    image.Dispose();
                    image = image2;
                }
                if (image.Width % 4 != 0)
                {
                    Image image2 = ImageUtil.ScaleImage(image, image.Width - (image.Width % 4), image.Height);
                    image.Dispose();
                    image = image2;
                }
                //人脸检测
                ASF_MultiFaceInfo multiFaceInfo = FaceUtil.DetectFace(pImageEngine, image);
                //判断检测结果
                if (multiFaceInfo.faceNum != 1)
                {
                    image.Dispose();
                    MessageBox.Show($"人脸过多，数量 : {multiFaceInfo.faceNum}");
                    return;
                }
                Image oldImg = pictureBoxSelected.Image;
                if (oldImg != null)
                {
                    pictureBoxSelected.Image = null;
                    oldImg.Dispose();
                }
                pictureBoxSelected.Image = image;
            }
            catch (Exception err)
            {
                MessageBox.Show(err.Message);
            }
        }

        private void btnRegisterFace_Click(object sender, EventArgs e)
        {
            try
            {
                if (string.IsNullOrEmpty(textBoxName.Text))
                {
                    MessageBox.Show("请输入姓名");
                    return;
                }
                //提取人脸特征
                Image image = pictureBoxSelected.Image;
                if (image == null)
                {
                    MessageBox.Show("请选择图片");
                    return;
                }
                ASF_MultiFaceInfo multiFaceInfo = FaceUtil.DetectFace(pImageEngine, image);
                IntPtr pCurrentFeature = FaceUtil.ExtractFeature(pImageEngine, image, multiFaceInfo, out ASF_SingleFaceInfo singleFaceInfo);

                imagesFeatureDictionary.Add(pCurrentFeature, textBoxName.Text);
                FaceUtil.EvetGetFeatureBytes(pCurrentFeature, out byte[] arr);
                string path = "./Features/" + textBoxName.Text;
                if (File.Exists(path))
                {
                    File.Delete(path);
                }
                File.WriteAllBytes("./Features/" + textBoxName.Text, arr);

                AppendText(string.Format(textBoxName.Text + " register success!\r\r\n"));
            }
            catch (Exception err)
            {
                MessageBox.Show(err.Message);
            }
        }

        private void btnSelectImageToRecognize_Click(object sender, EventArgs e)
        {
            try
            {
                OpenFileDialog openFileDialog = new OpenFileDialog();
                openFileDialog.Title = "Select";
                //openFileDialog.Filter = "Image File|*.bmp;*.jpg;*.jpeg;*.png";
                //openFileDialog.Multiselect = true;
                openFileDialog.FileName = string.Empty;
                if (openFileDialog.ShowDialog() == DialogResult.OK)
                {
                    string fileName = openFileDialog.FileName;
                    if (!CheckImage(fileName, out string errmsg))
                    {
                        MessageBox.Show(errmsg);
                        return;
                    }

                    IntPtr image1Feature = IntPtr.Zero;
                    pictureBoxToRecognize.ImageLocation = fileName;
                    Image srcImage = ImageUtil.ReadFromFile(fileName);

                    ASF_SingleFaceInfo singleFaceInfo = new ASF_SingleFaceInfo();
                    //提取人脸特征
                    image1Feature = FaceUtil.ExtractFeature(pImageEngine, srcImage, out singleFaceInfo);

                    if (imagesFeatureDictionary.Count == 0)
                    {
                        MessageBox.Show("请注册人脸!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Error);
                        return;
                    }

                    if (image1Feature == IntPtr.Zero)
                    {
                        if (pictureBoxToRecognize.Image == null)
                        {
                            MessageBox.Show("请选择识别图!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Error);
                        }
                        else
                        {
                            MessageBox.Show("比对失败，识别图未提取到特征值!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Error);
                        }
                        return;
                    }

                    string name = "无结果\r\n";
                    float similarity = 0f;
                    for (int i = 0; i < imagesFeatureDictionary.Count; i++)
                    {
                        IntPtr feature = imagesFeatureDictionary.ElementAt(i).Key;
                        float newSimilarity = 0f;
                        int ret = ASFFunctions.ASFFaceFeatureCompare(pImageEngine, image1Feature, feature, ref newSimilarity);
                        //增加异常值处理
                        if (newSimilarity.ToString().IndexOf("E") > -1)
                            newSimilarity = 0f;

                        if (newSimilarity > similarity)
                        {
                            similarity = newSimilarity;
                            name = imagesFeatureDictionary.ElementAt(i).Value;
                        }
                    }
                    if (similarity > threshold)
                    {
                        AppendText("对比结果：" + name + "  可信度：" + similarity + "\r\n");
                    }
                    else
                    {
                        AppendText("无结果\r\n");
                    }
                }
            }
            catch (Exception err)
            {
                MessageBox.Show(err.Message);
            }
        }

        private void AppendText(string message)
        {
            try
            {
                logBox.AppendText(message);
            }
            catch (Exception)
            {
            }
        }
        //检查文件是否为图片
        private bool CheckImage(string imagePath, out string errmsg)
        {
            errmsg = "";
            try
            {
                //判断图片是否正常，如将其他文件把后缀改为.jpg，这样就会报错
                Image image = ImageUtil.ReadFromFile(imagePath);
                image.Dispose();
            }
            catch
            {
                errmsg = "获取图片失败";
                return false;
            }
            return true;
        }

        BackgroundWorker bgw = new BackgroundWorker();
        private VideoSourcePlayer videoSourcePlayer1;
        FilterInfoCollection videoDevices;
        VideoCaptureDevice videoSource;
        private void btnVideo_Click(object sender, EventArgs e)
        {
            try
            {
                pnlVideo.Controls.Clear();
                videoSourcePlayer1 = new VideoSourcePlayer();
                pnlVideo.Controls.Add(videoSourcePlayer1);
                videoSourcePlayer1.Dock = DockStyle.Fill;
                videoDevices = new FilterInfoCollection(FilterCategory.VideoInputDevice);
                if (videoDevices.Count == 0)
                {
                    MessageBox.Show("未找到摄像头");
                    return;
                }
                videoSource = new VideoCaptureDevice(videoDevices[0].MonikerString);
                videoSourcePlayer1.VideoSource = videoSource;
                videoSourcePlayer1.SignalToStop();
                videoSourcePlayer1.WaitForStop();
                videoSourcePlayer1.Start();
                bgw.RunWorkerAsync();
            }
            catch (Exception err)
            {
                MessageBox.Show(err.Message);
            }
        }

        private void btnStopVideo_Click(object sender, EventArgs e)
        {
            try
            {
                if (bgw.IsBusy)
                {
                    bgw.CancelAsync();
                }
            }
            catch (Exception)
            {
                MessageBox.Show("关闭摄像头失败");
            }
        }

        private void Bgw_DoWork(object sender, DoWorkEventArgs e)
        {
            while (true)
            {
                try
                {
                    Thread.Sleep(600);
                    GC.Collect();
                    if (bgw.CancellationPending)
                    {
                        return;
                    }
                    if (videoSource == null)
                    {
                        continue;
                    }
                    if (imagesFeatureDictionary.Count == 0)
                    {
                        continue;
                    }
                    Bitmap bitmap = videoSourcePlayer1.GetCurrentVideoFrame();
                    if (bitmap == null)
                    {
                        continue;
                    }
                    using (Image img = new Bitmap(bitmap))
                    {
                        bitmap.Dispose();

                        ImageInfo imageInfo = ImageUtil.ReadBMP(img);
                        if (imageInfo == null)
                        {
                            continue;
                        }
                        ASF_MultiFaceInfo multiFaceInfo = FaceUtil.DetectFace(pImageEngine, imageInfo);
                        if (multiFaceInfo.faceNum <= 0)
                        {
                            MemoryUtil.Free(imageInfo.imgData);
                            bgw.ReportProgress(0, $"无人脸\r\n");
                            continue;
                        }
                        bgw.ReportProgress(0, $"facenum:{multiFaceInfo.faceNum}\r\n");
                        if (multiFaceInfo.faceNum == 1)
                        {
                            lock (mylock)
                            {
                                if (this.needCamPic)//截取摄像头图片
                                {
                                    bgw.ReportProgress(1, ImageUtil.CopyBitmap(img as Bitmap));
                                    this.needCamPic = false;
                                }
                            }
                        }
                        ASF_LivenessInfo liveinfo = FaceUtil.LivenessInfo_RGB(pLiveEngine, imageInfo, multiFaceInfo, out int retCode);

                        //ASF_LivenessInfo liveinfo = FaceUtil.LivenessInfo_IR(pImageEngine, imageInfo, multiFaceInfo, out int retCode);
                        if (retCode == 0 && liveinfo.num > 0)
                        {
                            int isLive = MemoryUtil.PtrToStructure<int>(liveinfo.isLive);
                            if (isLive == 0)
                            {
                                MemoryUtil.Free(imageInfo.imgData);
                                bgw.ReportProgress(0, $"非活体\r\n");
                                continue;
                            }
                            if (isLive == -1)
                            {
                                MemoryUtil.Free(imageInfo.imgData);
                                bgw.ReportProgress(0, $"不确定是否活体\r\n");
                                continue;
                            }
                            if (isLive != 1)
                            {
                                MemoryUtil.Free(imageInfo.imgData);
                                bgw.ReportProgress(0, $"不确定是否活体\r\n");
                                continue;
                            }
                        }
                        List<IntPtr> pFeatureList = FaceUtil.ExtractMultiFeature(pImageEngine, imageInfo, multiFaceInfo);
                        MemoryUtil.Free(imageInfo.imgData);
                        foreach (IntPtr pFeature in pFeatureList)
                        {
                            if (pFeature == IntPtr.Zero)
                            {
                                continue;
                            }
                            string name = "无结果";
                            float similarity = 0f;
                            for (int i = 0; i < imagesFeatureDictionary.Count; i++)
                            {
                                IntPtr feature = imagesFeatureDictionary.ElementAt(i).Key;
                                float newSimilarity = 0f;
                                int ret = ASFFunctions.ASFFaceFeatureCompare(pImageEngine, pFeature, feature, ref newSimilarity);
                                //增加异常值处理
                                if (newSimilarity.ToString().IndexOf("E") > -1)
                                    newSimilarity = 0f;

                                if (newSimilarity > similarity)
                                {
                                    similarity = newSimilarity;
                                    name = imagesFeatureDictionary.ElementAt(i).Value;
                                }
                            }
                            ASF_FaceFeature localFeature = MemoryUtil.PtrToStructure<ASF_FaceFeature>(pFeature);
                            MemoryUtil.Free(localFeature.feature);
                            MemoryUtil.Free(pFeature);
                            if (similarity > threshold)
                            {
                                bgw.ReportProgress(0, "对比结果：" + name + "  可信度：" + similarity + "");
                            }
                            else
                            {
                                bgw.ReportProgress(0, "无结果");
                            }
                        }
                        bgw.ReportProgress(0, "\r\n");
                    }
                }
                catch (Exception)
                {
                    return;
                }
            }
        }
        //0显示文字，  1截取视频中的图片用于注册
        private void Bgw_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            if (e.ProgressPercentage == 0)
            {
                AppendText(e.UserState as string);
            }
            if (e.ProgressPercentage == 1)
            {
                Bitmap b = e.UserState as Bitmap;
                try
                {
                    Image image = new Bitmap(b);
                    if (image.Width > 1536 || image.Height > 1536)
                    {
                        Image image2 = ImageUtil.ScaleImage(image, 1536, 1536);
                        image.Dispose();
                        image = image2;
                    }

                    if (image.Width % 4 != 0)
                    {
                        Image image2 = ImageUtil.ScaleImage(image, image.Width - (image.Width % 4), image.Height);
                        image.Dispose();
                        image = image2;
                    }

                    //人脸检测
                    ASF_MultiFaceInfo multiFaceInfo = FaceUtil.DetectFace(pImageEngine, image);
                    //判断检测结果
                    if (multiFaceInfo.faceNum != 1)
                    {
                        image.Dispose();
                        MessageBox.Show($"人脸过多，数量 : {multiFaceInfo.faceNum}");
                        return;
                    }
                    Image oldImg = pictureBoxSelected.Image;
                    if (oldImg != null)
                    {
                        pictureBoxSelected.Image = null;
                        oldImg.Dispose();
                    }
                    pictureBoxSelected.Image = image;
                }
                catch (Exception err)
                {
                    MessageBox.Show(err.Message);
                }
            }
        }

        private void Bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            try
            {
                pnlVideo.Controls.Clear();
                videoSourcePlayer1.SignalToStop();
                videoSourcePlayer1.WaitForStop();
                videoSourcePlayer1.Stop();
                videoSourcePlayer1.Dispose();
            }
            catch (Exception)
            {
            }
        }

        private void btnFromCam_Click(object sender, EventArgs e)
        {
            lock (mylock)
            {
                needCamPic = true;
            }
        }

        private void MainFrm_FormClosing(object sender, FormClosingEventArgs e)
        {
            try
            {
                if (bgw.IsBusy)
                {
                    bgw.CancelAsync();
                }
            }
            catch (Exception)
            {
            }
            try
            {
                pnlVideo.Controls.Clear();
                videoSourcePlayer1.SignalToStop();
                videoSourcePlayer1.WaitForStop();
                videoSourcePlayer1.Stop();
                videoSourcePlayer1.Dispose();
            }
            catch (Exception)
            {
            }
        }
    }
}
